Fruit教程翻译: 2. 简单系统, 2. Simple system
这是教程的第二部分, 我们会实现一个简单的命令行工具, 它读入一个数字, 将该数字加1,然后输出结果。 在实际的系统中,如此简单的功能 ,一般会写在单个类中 (甚至直接 写在 main() 函数中 ) 。 而在此教程中, 我们 (过度)使用依赖注入技术 , 来将各个功能进行 尽可能细致 地划分, 以向妳展示, Fruit能够帮助 妳利用多个组件构建 出一个复杂系统,同时限制各个组件之间 的依赖关系。
在下面的示例代码中,我们省略掉了那些不相关的代码 (例如,include保护代码 ,以及系统头文件的 include代码) 。完整 的源代码在这里: examples/simple_injection 。
首先,编写一个接口,表示数字加1:
// incrementer.h
class Incrementer {
public:
// 返回x + 1
。
virtual int increment(int x) = 0;
};
数字加1,其实是加法的一个特例,因此,让我们也编写一个接口,表示加法:
// adder.h
class Adder {
public:
// 返回
x
与
y
的和。
virtual int add(int x, int y) = 0;
};
现在,我们想要这样一个组件,当向它提供一个Adder 实现的时候,它会提供一个Incrementer 实现。
// incrementer_impl.h
#include "incrementer.h"
#include "adder.h"
fruit::Component<fruit::Required<Adder>, Incrementer> getIncrementerImplComponent();
// incrementer_impl.cpp
#include "incrementer_impl.h"
class IncrementerImpl : public Incrementer {
private:
Adder* adder;
public:
INJECT(IncrementerImpl(Adder* adder))
: adder(adder) {
}
virtual int increment(int x) override {
return adder->add(x, 1);
}
};
fruit::Component<fruit::Required<Adder>, Incrementer> getIncrementerImplComponent() {
return fruit::createComponent()
.bind<Incrementer, IncrementerImpl>();
}
这是我们第一次面对那种本身 有要求的组件。 Fruit组件 是可以有类型要求的, 这些类型,以 fruit::Required<T1, ..., Tn> 的形式在该组件的第一个类型参数中指定。如果 getIncrementerImplComponent() 的原型中未指定要求,那么, Fruit 会寻找针对Adder 的绑定,并且 ,由于未能找到而终止编译 ,报告错误。对于每个组件 ,它尚未绑定的所有必需的类型,都必须被暴露。
注意 , 头文件中暴露的唯一信息就是, 该模块要求什么、提供什么。 IncrementerImpl 类,只在 .cpp文件 中定义。 这与 透明指针 类似。
注意:习惯了C++风格的读者,可能希望用一个匿名的命名空间来将这个类包装起来。这是一个合理的预期,因为这个类仅仅在该.cpp文件中使用。出于易读性考虑,我们省略掉了这个匿名的命名空间。
同时,这里也缺少了虚析构函数,这也是有意为之,因为,Fruit会在注入器中处理各个对象的析构过程,它会自动销毁那些实体(concrete)类(不是利用指向基类的指针来实现的)。即使妳打开了"-W -Wall"选项,编译器也不会报告警告。
现在,让我们来实现Adder。
// simple_adder.h
#include "adder.h"
fruit::Component<Adder> getSimpleAdderComponent();
// simple_adder.cpp
#include "simple_adder.h"
class SimpleAdder : public Adder {
public:
INJECT(SimpleAdder()) = default;
virtual int add(int x, int y) override {
return x + y;
}
};
fruit::Component<Adder> getSimpleAdderComponent() {
return fruit::createComponent()
.bind<Adder, SimpleAdder>();
}
这个组件狠简单,它的代码已经自解释了。
现在, 我们拥有了一个能够提供 Adder 的组件, 以及一个需要 Adder 而又提供了 Incrementer 的组件。 ,现在,我们要将这两个组件复合起来。
// simple_incrementer.h
#include "incrementer.h"
fruit::Component<Incrementer> getSimpleIncrementerComponent();
// simple_incrementer.cpp
#include "simple_incrementer.h"
#include "incrementer_impl.h"
#include "simple_adder.h"
fruit::Component<Incrementer> getSimpleIncrementerComponent() {
return fruit::createComponent()
.install(getIncrementerImplComponent())
.install(getSimpleAdderComponent());
}
install() 是一个操作函数,它将 一个子组件“安装”到当前组件中去。 我们在上一页教程中已经观摩过相应的处理过程了,在这里, 我们并不将所有能够暴露的接口全部都暴露出去。 Adder接口, 被认为是一个与实现相关的细节,因此 ,我们并不暴露它。注意 ,它甚至并没有被包含到头文件中,仅仅 是 .cpp文件依赖 它 ( 通过 simple_adder.h 间接地依赖 ) 。 这个例子展示了, Fruit能够帮助 妳减少 大型项目中 include指令 的个数 (因而缩短 了编译时间 ) 。 如果不使用依赖注入,那么, 为了暴露出Incrementer 的实现, 我们需要包含 IncrementerImpl 的头文件, 而 IncrementerImpl实现代码 中又需要包含 SimpleAdder 的头文件。如果使用依赖注入 ,但不使用 Fruit ,那么, IncrementerImpl 的实现代码中不需要包含 SimpleAdder 的头文件,但是,客户代码中还是需要 同时 包含IncrementerImpl 和SimpleAdder 的头文件。
啊!数字
加1,实现完毕。
现在,实现部分
也编写完毕,我们只需要编写
main()
函数了。
#include "simple_incrementer.h"
using fruit::Component;
using fruit::Injector;
int main() {
Injector<Incrementer> injector(getSimpleIncrementerComponent());
Incrementer* incrementer = injector.get<Incrementer*>();
int x;
std::cin >> x;
std::cout << incrementer->increment(x) << std::endl;
return 0;
}
我们利用该组件构造了一个Injector,然后,通过该注入器获取了一个Incrementer 实例。这里,不需要包含任何的类定义头文件。
在以高昂的价格(或者开源, 这取决于妳的喜好 )发布了该程序之后, 我们收到了用户的一些反馈。部分用户 用得狠开心,但是 ,也有一部分用户注意到了一个问题, 对某个巨大的数字加1之后,会产生一个负数。 它们希望我 们加入一个 --checked 选项,用于启用溢出检 查。
我们的实现代码是模块化的(感谢依赖注入),因此,我们不需要对以上的任何组件进行修改。同时,Incrementer组件将具体的工作委托给了Adder,因此,我们此刻唯一要做的就是,对于Adder 写出一种会检查溢出情况的实现。
// checked_adder.h
#include "adder.h"
fruit::Component<Adder> getCheckedAdderComponent();
// checked_adder.cpp
#include "checked_adder.h"
class CheckedAdder : public Adder {
private:
bool add_overflows(int x, int y) {
... // 具体 的实现代码
}
public:
INJECT(CheckedAdder()) = default;
virtual int add(int x, int y) override {
if (add_overflows(x, y)) {
std::cerr << "CheckedAdder: detected overflow during addition of " << x << " and " << y << std::endl;
abort();
}
return x + y;
}
};
fruit::Component<Adder> getCheckedAdderComponent() {
return fruit::createComponent()
.bind<Adder, CheckedAdder>();
}
并没有什么新鲜东西。现在,我们将这个新的组件与之前写好的IncrementComponent 组合起来。
// checked_incrementer.h
#include "incrementer.h"
fruit::Component<Incrementer> getCheckedIncrementerComponent();
// checked_incrementer.cpp
#include "checked_incrementer.h"
#include "incrementer_impl.h"
#include "checked_adder.h"
fruit::Component<Incrementer> getCheckedIncrementerComponent() {
return fruit::createComponent()
.install(getIncrementerImplComponent())
.install(getCheckedAdderComponent());
}
除了对 IncrementerComponent 的复用之外, 没有别的什么需要注意的东西了。
// incrementer_component.h
#include "incrementer.h"
fruit::Component<Incrementer> getIncrementerComponent(bool checked);
// incrementer_component.cpp
#include "incrementer_component.h"
#include "simple_incrementer.h"
#include "checked_incrementer.h"
fruit::Component<Incrementer> getIncrementerComponent(bool checked) {
if (checked)
return getCheckedIncrementerComponent();
else
return getSimpleIncrementerComponent();
}
get * Component() 仅仅 是一个函数而已,因此, 我们可以让它提供出参数化的组件, 只需要 向这个函数加入参数,并且在函数中使用 if 判断 来引入 多个return语句。
现在,让我们重写main()函数,以接受新的参数,并且使用新的参数化的模块。
// main.cpp
#include "incrementer_component.h"
using fruit::Component;
using fruit::Injector;
// Try
一try:
// echo 5 | ./incrementer
// echo 2147483647 | ./incrementer
// echo 2147483647 | ./incrementer --checked
int main(int argc, const char* argv[]) {
bool checked = false;
if (argc == 2 && std::string(argv[1]) == "--checked")
checked = true;
Injector<Incrementer> injector(getIncrementerComponent(checked));
Incrementer* incrementer(injector);
int x;
std::cin >> x;
std::cout << incrementer->increment(x) << std::endl;
return 0;
}
此处 ,我们展示了另一种利用注入器来获取类实例的语法。 我们不是直接调用注入器的 get ,而是, 将注入器转换成我们想要的那种类型。 这是一种便利实现方法,用来避免 将类型重复两次 ( 与上面的 main函数对比 一下 ) 。
恒星
高圆圆
未知美人
Your opinionsHxLauncher: Launch Android applications by voice commands